1 Functions Introduction

1.1 Function Structure

One of the most useful parts of R programming is the ability to write custom functions. This gives the user the flexibility to automate any task that they need. To start, let’s open up one of the functions from the RUO package to see what’s going on:

APPLY.BND() # This function won't run
function (DATA, RBND, VARS = NULL, Uniq_Keys, USE_RED = FALSE,KEEP_DATA = TRUE) {}

The first thing we’ll notice is the word ‘function’ followed by a set of rounded brackets and finally some curly braces. Within the rounded brackets are the function’s ARGUMENTS and the curly braces contain the BODY of the function.

Creating a function is just like you would assign a value to a variable, except you now include the word “function”.

For example:

x <- 2
x <- function() {}

We have just created a function called x.

1.2 Function Arguments

Arguments provide a way for the user to control the variables within a function without having to modify the source code.

Let’s look at an example:

returnSum <- function(x, y) {
  return(x + y) # Return will be covered soon
}

Our example function has two arguments, x and y. We can tell returnSum that x and y are any numbers and it will give us back the sum of those two numbers.

returnSum(3, 5)
[1] 8
returnSum(7, 16)
[1] 23
returnSum(2, 8)
[1] 10

Without arguments, we would have to hard code x and y and then our functions wouldn’t be very useful.

returnSum <- function () {
  return(4)
}
returnSum()
[1] 4

Arguments don’t have to just be integers, they can be any data type, even other functions. The only restriction is that all operations within the function have to work on that data type.

Look back at our original example, APPLY.BND. Notice that some of the arguments are assigned values and some are not?

function (DATA, RBND, VARS = NULL, Uniq_Keys, USE_RED = FALSE,KEEP_DATA = TRUE)

The arguments with pre- assigned values are called ‘Default Arguments’. These behave just like regular arguments, except if you don’t assign them a value they will take on the default value (hence the name.)

returnSum <- function(x, y, z = 5) {
  return(x + y + z)
}
returnSum(2, 3)
[1] 10

Note, you can still assign any value to default arguments, it will overwrite the default.

returnSum(2, 3, 6)
[1] 11

You’ll typically use default arguments when you don’t expect the value of that variable to change often. The user won’t need to specify the argument everytime they call the function.

IMPORTANT

The order of the arguments matters if you’re not explicitly specifying them. Notice when we’ve been calling our functions, we haven’t been writing x = 2, y = 3, etc. This is because we’re sticking to the order in which they appear within the function definition.

#Look at what happens if we don't respect the order:
returnSum <- function(x, y = 5, z) {
  return(x + y + z)
}

returnSum(4, 6)
#This will not run

Will return: Error in returnSum(4, 6) : argument “z” is missing, with no default

Y has a default value, but because we haven’t specified assignments, 6 is being assigned to y (overwriting the default) and z is left undefined. To prevent this, explicitly specify your assignments:

returnSum(4, z = 6)
[1] 15

However since we specified that z=6, the code will run.

Good practice dictates that you should start making your assignments explicit after the second argument; which also means if you’re writing functions, don’t put default values for your first two arguments.

Let’s dive a bit more into the body of the function now. At their core, functions are a set of instructions performed on a set of variables to generate a desired outcome. We’ve just covered arguments, where you assign values to the set of variables, so logicaly the body is where we define the set of instructions.

1.3 EDA Function

Let’s refer to the EDA lesson and see if we can convert some parts into functions. A good rule of thumb is that if you’re repeating something 3 or more times, you can probably convert it into a function.

Let’s look at the Data Manipulation step of the EDA lesson. Specifically, this chunk of code:

AGG.VEH <- DATA_Scored %>%
  group_by(Clt_Insurer_Tx,
           Clt_Account_No, 
           Pol_Policy_No, 
           Veh_Id_No) %>%
  summarise(LC_Veh_Total = sum(LC_TOTAL))

AGG.INSURER <- DATA_Scored %>% 
  left_join(AGG.VEH) %>%
  filter(Clo_Sale_In == 1 & Dri_Type_Cd == 'P') %>% # To have the data by vehicle
  select(LC_Veh_Total,
         Prm_Trm_Veh_Tot_Am,
         Clt_Insurer_Tx) %>% 
  group_by(Clt_Insurer_Tx) %>% 
  summarise(Prm_Trm_Veh_Mean = mean(Prm_Trm_Veh_Tot_Am),
            LR_Veh_Mean  = sum(LC_Veh_Total, na.rm = TRUE)/sum(Prm_Trm_Veh_Tot_Am),
            Xpo_Veh_Nb   = sum(n()))
print(AGG.INSURER)
AGG.DRI_AGE <- DATA_Scored %>% 
  left_join(AGG.VEH) %>%
  filter(Clo_Sale_In == 1 & Dri_Type_Cd == 'P') %>% # To have the data by vehicle
  select(LC_Veh_Total,
         Prm_Trm_Veh_Tot_Am,
         Dri_Marital_Status_In,
         Dri_Age_Nb) %>% 
  group_by(Dri_Marital_Status_In,
           Dri_Age_Nb) %>% 
  summarise(Prm_Trm_Veh_Mean = mean(Prm_Trm_Veh_Tot_Am),
            LR_Veh_Mean  = sum(LC_Veh_Total, na.rm = TRUE)/sum(Prm_Trm_Veh_Tot_Am),
            Xpo_Veh_Nb   = sum(n()))
head(AGG.DRI_AGE)

Function for Loss Ratios by Vehicle

Assuming we’re interested in being able to look at our loss ratios by driver but want to be able to conduct the analysis multiple times, we would need our data, variables and driver type to be flexible, so we thus include these as arguments in our funciton.

aggregateVars <- function(DATA, KEEP.VARS, GROUP.VARS, DRIVER.TYPE = 'P') {
  
  # It doesn't matter what you call your data within the function, 
  # I like to use temp
  temp <- DATA %>% 
    left_join(AGG.VEH) %>%
    filter(Clo_Sale_In == 1 & Dri_Type_Cd == DRIVER.TYPE) %>% 
    select(LC_Veh_Total, # Required var
           Prm_Trm_Veh_Tot_Am, # Required var
           # one_of is a dplyr function designed to be used with Select
           one_of(KEEP.VARS)) %>% 
    # Notice we're using group_by_at instead of group_by now
    # Some dplyr functions have versions designed for functional 
    # programming; this is one.
    group_by_at(GROUP.VARS) %>% 
    summarize(Prm_Trm_Veh_Mean = mean(Prm_Trm_Veh_Tot_Am),
              LR_Veh_Mean  = sum(LC_Veh_Total, na.rm = TRUE)/sum(Prm_Trm_Veh_Tot_Am),
              Xpo_Veh_Nb   = sum(n()))
  
  # We'll cover return in a moment
  return(temp)
}

Now we can define our KEEP.VARS and GROUP.VARS We’re defining the column names as strings, R knows what to do though

KEEP.VARS <- c("Dri_Marital_Status_In", "Dri_Age_Nb")
GROUP.VARS <- KEEP.VARS 

And call our function

AGG.DRI_AGE_Function_Out <- aggregateVars(
  DATA = DATA_Scored, 
  KEEP.VARS, 
  GROUP.VARS = GROUP.VARS
  )

Notice that this outputs the exact same thing as the above chunk of code. The difference is, now we can perform the same analysis on different variables with minimal effort.

KEEP.VARS <- c("Dri_Gender_Cd", "Dri_Yrs_Licensed_AU_Nb")
GROUP.VARS <- KEEP.VARS 

AGG.Yrs_Lic_Function_Out <- aggregateVars(
  DATA_Scored, 
  KEEP.VARS, 
  GROUP.VARS = GROUP.VARS
)

Hopefully this example has demonstrated the flexibility and usefulness of creating custom functions. The key-takeaway is that if you can script it, you can turn it into a function for re-useability.

1.4 Variable Scope

Variable scope is a pretty complex topic, but for most use cases in our job knowing the basics is sufficient. The easiest way to explain variable scope is with an example.

Observe

y <- 7

demoFunction <- function(x) {

  print(paste("The value of x is ", x, " and the value of Y is ", y), sep = "")
  
}
demoFunction(8)
[1] "The value of x is  8  and the value of Y is  7"
rm(y) # To delete y

We can see that even though y is not defined within the function, demoFunction is still able to find the y value that was declared outside of the function. This is because of variable scope. By default, the user is working in something called the Global Environment and functions will have access to the variables declared in our global environment. Now if we want to look at another example:

y <- 6
demoFunction2 <- function() {
  y <- 4
  y
}
demoFunction2()
[1] 4

We can see that the function assigns y a value of 4 within the function and will return this value of y which is 4.

y
[1] 6

However, when we look at y outside of the function y is still 6. This again has to do with the environment and the fact that assigning y the value of 4 within the function will not change the value of y in the global environment.

1.5 Variable Returns

We mentioned above through our most recent example that enviornments do not pass all their objects onto the global environment. So how do you get objects from a child environment to its parent?

The answer is with the return function. When you call a function, it is going to return something at the end of its run. It may create many objects along the way, but at its end it will only return one object ( that object can be a list of many objects.) The return function is your way of telling function what you want them to return.

returnValue <- function(x, y) {
  
  z <- x * y
  
  return(z)
}
returnValue(2, 5)
[1] 10

It is not entirely necessary to use use return() in functions. If there is no return specified, your function will return the last thing it calls internally.

returnValue <- function(x, y) {
  
  z <- x * y
  
  z
}
returnValue(2, 5)
[1] 10

The above function will work just fine and will return z, just as we want. Using return() is only recommended because it helps with comprehension of your code. If you’re writing functions which will be used by users other than yourself, it is a good idea to include return() calls.

Finally, because functions are returning a single object, you need to make sure you’re storing that object in your global environment if you intend to use it later.

w <- returnValue(2, 5) # Store the output object from returnValue in a variable called w
p <- w*3
p
[1] 30

2 Examples

We’ve been able to see some functions and have determined that they are useful for resuability of code and performing tasks. We want to continue this mind set and see how some simple code can be saved to tackle problems that will be faced multiple times.

2.1 Bivariate Analysis

2.1.1 Explanation

Let us see a simple example of a function that will perform an initial bivariate analysis of our data set. We want something flexible that will allow us to perform analysis regardless of our data set and response variable.

The arguments for this function include:

  1. DATA: The data that we will be using. exp: DATA = DATA_QUO_AUTO

  2. x: The name of the x varaible. exp: x = Dri_Age_Nb

  3. y: The name of the y variable. Examples such as loss cost or closing, exp: y = Clo_Sale_In

These functions below will combine what has been seen so far to provide some examples of easy to use functions.

2.1.2 Breakdown of Function Example

This function example here provides users with the ability to see how a response variable such as closing is different across a variable of our choice. We will keep y as a default variable of Clo_Sale_In but as previously mentioned the y argment can be changed if needed.

Don’t worry too much about !! and enquo, but this is simply a technique we can use to pass the name of the data frame variable without using quotations "".

summaryStats <- function(DATA,x,y = Clo_Sale_In){
  DATA %>%
    group_by(!! enquo(x)) %>%
    summarize(Mean = mean(!!enquo(y), na.rm=TRUE),
              Count = n())
}
summaryStats(DATA_QUO_AUTO,Clt_Home_Main_Location_Cd)
summaryStats(DATA_QUO_AUTO,Clt_Segment)
summaryStats(DATA_QUO_AUTO,Veh_Model_Yr_Dt,Clo_Sale_In)

2.2 Plotting

We will use the plotting function plotly as seen in previous examples that will allow us to have a flexible function for y vs x plotting. We can created the grouped data in the exact same was as previously before we plot it

plotlyData <- function(DATA,x,y = Clo_Sale_In) {

Grouped_Data = DATA %>%
  group_by(!!enquo(x)) %>%
  summarize(Response_Mean = mean(!!enquo(y), na.rm=TRUE),
            Count = n())
p <- plot_ly(data = Grouped_Data) %>%
  #add_lines will provide the mean response at each group
  add_lines(
    x =  enquo(x),
    y =~ Response_Mean,
    name ="Observed Mean"
  ) %>% 
  add_bars(
    x =  enquo(x),
    y =~  Count,
    yaxis = "y2",
    opacity = 0.35,
    name = "Exposure"
  ) %>%
  layout(
    title = paste0(enexpr(y), " vs ", enexpr(x)),
    xaxis = list(title = paste0(enexpr(x))),
    yaxis = list(title = paste0(enexpr(y)), side = "left", overlaying = "y2"),
    barmode = "stack",
    yaxis2 = list(title = "Exposure", side = "right")
  )
return(p)
}

We can now try out our plot function to have plots generated and saved in respective variables.

Plot_Dri_Age_Nb <- plotlyData(DATA_QUO_AUTO,Dri_Age_Nb)
Plot_Clt_Home_Main_Location_Cd<-plotlyData(DATA_QUO_AUTO,Clt_Home_Main_Location_Cd)
Plot_Dri_Ubi_In <- plotlyData(DATA_QUO_AUTO,Dri_Ubi_In)

And we can now look at these plots.

Plot_Dri_Age_Nb
Plot_Clt_Home_Main_Location_Cd
Plot_Dri_Ubi_In

These are 3 examples of functions that you could create yourself. From the use of other functions and previously written code, you could create functions tailored to your everyday needs.

Finally we want to try to export these plots.

#Change this to a path that works for you to export to
setwd("C:/Users/opti1407/OneDrive - The Toronto-Dominion Bank/Documents")

htmlwidgets::saveWidget(Plot_Dri_Age_Nb, "Plot_Dri_Age_Nb.html")
htmlwidgets::saveWidget(Plot_Clt_Home_Main_Location_Cd, "Plot_Clt_Home_Main_Location_Cd.html")
htmlwidgets::saveWidget(Plot_Dri_Ubi_In, "Plot_Dri_Ubi_In.html")

In summary once we’ve written code whether it be for rebasing, scoring or quickly analysing our data, we can convert this code into a function to allow us to use it for a variety of problems.

LS0tDQp0aXRsZTogJ1IgVHJhaW5pbmc6IDAzIC0gRnVuY3Rpb25zJw0KYXV0aG9yOiAiUHJpY2luZyBJbm5vdmF0aW9uIFRlYW0iDQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6DQogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMNCiAgICB0aGVtZTogZmxhdGx5DQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZmxvYXQ6IHllcw0KLS0tDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQ0KYGBgDQoNCmBgYHtyIGVjaG89RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpsaWJyYXJ5KGZzdCkNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KGh0bWx3aWRnZXRzKQ0KbGlicmFyeShmb3JjYXRzKQ0KbGlicmFyeShwbG90bHkpDQpsaWJyYXJ5KFJVTykNCg0KUEFUSCA8LSAiUzovUHJpY2luZy9EYXRhL0RlbWFuZC8iDQpEQVRBX1FVT19BVVRPIDwtIHJlYWRfZnN0KHBhc3RlMChQQVRILCJDbG9zaW5nX2FfY3dfMjAxODEwMzFfYnlfZHJpdmVyX0NUUl9RQy5mc3QiKSkNCkRBVEFfU2NvcmVkIDwtIHJlYWRfZnN0KCJTOi9QcmljaW5nL0lubm92YXRpb24vUiBSZXNzb3VyY2VzL1IgLSBUcmFpbmluZyAyLjAvVHJhaW5pbmcvZGF0YS9EQVRBX1NDT1JFRC5mc3QiKQ0KYGBgDQoNCiMgRnVuY3Rpb25zIEludHJvZHVjdGlvbg0KDQojIyBGdW5jdGlvbiBTdHJ1Y3R1cmUNCg0KT25lIG9mIHRoZSBtb3N0IHVzZWZ1bCBwYXJ0cyBvZiBSIHByb2dyYW1taW5nIGlzIHRoZSBhYmlsaXR5IHRvIHdyaXRlIGN1c3RvbSBmdW5jdGlvbnMuIFRoaXMgZ2l2ZXMgdGhlIHVzZXIgdGhlIGZsZXhpYmlsaXR5IHRvIGF1dG9tYXRlIGFueSB0YXNrIHRoYXQgdGhleSBuZWVkLg0KVG8gc3RhcnQsIGxldCdzIG9wZW4gdXAgb25lIG9mIHRoZSBmdW5jdGlvbnMgZnJvbSB0aGUgUlVPIHBhY2thZ2UgdG8gc2VlIHdoYXQncyBnb2luZyBvbjoNCg0KYGBge3IsIGV2YWw9IEZBTFNFfQ0KQVBQTFkuQk5EKCkgIyBUaGlzIGZ1bmN0aW9uIHdvbid0IHJ1bg0KZnVuY3Rpb24gKERBVEEsIFJCTkQsIFZBUlMgPSBOVUxMLCBVbmlxX0tleXMsIFVTRV9SRUQgPSBGQUxTRSxLRUVQX0RBVEEgPSBUUlVFKSB7fQ0KYGBgDQoNClRoZSBmaXJzdCB0aGluZyB3ZSdsbCBub3RpY2UgaXMgdGhlIHdvcmQgJ2Z1bmN0aW9uJyBmb2xsb3dlZCBieSBhIHNldCBvZiByb3VuZGVkIGJyYWNrZXRzIGFuZCBmaW5hbGx5IHNvbWUgY3VybHkgYnJhY2VzLiBXaXRoaW4gdGhlIHJvdW5kZWQgYnJhY2tldHMgYXJlIHRoZSBmdW5jdGlvbidzIEFSR1VNRU5UUyBhbmQgdGhlIGN1cmx5IGJyYWNlcyBjb250YWluIHRoZSBCT0RZIG9mIHRoZSBmdW5jdGlvbi4NCg0KQ3JlYXRpbmcgYSBmdW5jdGlvbiBpcyBqdXN0IGxpa2UgeW91IHdvdWxkIGFzc2lnbiBhIHZhbHVlDQp0byBhIHZhcmlhYmxlLCBleGNlcHQgeW91IG5vdyBpbmNsdWRlIHRoZSB3b3JkICJmdW5jdGlvbiIuDQoNCkZvciBleGFtcGxlOg0KYGBge3J9DQp4IDwtIDINCnggPC0gZnVuY3Rpb24oKSB7fQ0KYGBgDQoNCldlIGhhdmUganVzdCBjcmVhdGVkIGEgZnVuY3Rpb24gY2FsbGVkIHguDQoNCiMjIEZ1bmN0aW9uIEFyZ3VtZW50cw0KDQpBcmd1bWVudHMgcHJvdmlkZSBhIHdheSBmb3IgdGhlIHVzZXIgdG8gY29udHJvbCB0aGUgdmFyaWFibGVzIHdpdGhpbiBhIGZ1bmN0aW9uIHdpdGhvdXQgaGF2aW5nIHRvIG1vZGlmeSB0aGUgc291cmNlIGNvZGUuDQogDQoNCkxldCdzIGxvb2sgYXQgYW4gZXhhbXBsZToNCg0KYGBge3IgZWNobyA9IFRSVUV9DQpyZXR1cm5TdW0gPC0gZnVuY3Rpb24oeCwgeSkgew0KICByZXR1cm4oeCArIHkpICMgUmV0dXJuIHdpbGwgYmUgY292ZXJlZCBzb29uDQp9DQpgYGANCg0KT3VyIGV4YW1wbGUgZnVuY3Rpb24gaGFzIHR3byBhcmd1bWVudHMsIHggYW5kIHkuIFdlIGNhbiB0ZWxsIHJldHVyblN1bSB0aGF0IHggYW5kIHkgYXJlIGFueSBudW1iZXJzIGFuZCBpdCB3aWxsIGdpdmUgdXMgYmFjayB0aGUgc3VtIG9mIHRob3NlIHR3byBudW1iZXJzLiANCg0KYGBge3J9DQpyZXR1cm5TdW0oMywgNSkNCmBgYA0KYGBge3J9DQpyZXR1cm5TdW0oNywgMTYpDQpgYGANCmBgYHtyfQ0KcmV0dXJuU3VtKDIsIDgpDQpgYGANCg0KV2l0aG91dCBhcmd1bWVudHMsIHdlIHdvdWxkIGhhdmUgdG8gaGFyZCBjb2RlIHggYW5kIHkgYW5kIHRoZW4gb3VyIGZ1bmN0aW9ucyB3b3VsZG4ndCBiZSB2ZXJ5IHVzZWZ1bC4gDQoNCmBgYHtyIGVjaG8gPSBUUlVFfQ0KcmV0dXJuU3VtIDwtIGZ1bmN0aW9uICgpIHsNCiAgcmV0dXJuKDQpDQp9DQpgYGANCg0KYGBge3J9DQpyZXR1cm5TdW0oKQ0KYGBgDQoNCkFyZ3VtZW50cyBkb24ndCBoYXZlIHRvIGp1c3QgYmUgaW50ZWdlcnMsIHRoZXkgY2FuIGJlIGFueSBkYXRhIHR5cGUsIGV2ZW4gb3RoZXIgZnVuY3Rpb25zLiBUaGUgb25seSByZXN0cmljdGlvbiBpcyB0aGF0IGFsbCBvcGVyYXRpb25zIHdpdGhpbiB0aGUgZnVuY3Rpb24gaGF2ZSB0byB3b3JrIG9uIHRoYXQgZGF0YSB0eXBlLg0KDQpMb29rIGJhY2sgYXQgb3VyIG9yaWdpbmFsIGV4YW1wbGUsIEFQUExZLkJORC4gTm90aWNlIHRoYXQgc29tZSBvZiB0aGUgYXJndW1lbnRzIGFyZSBhc3NpZ25lZCB2YWx1ZXMgYW5kIHNvbWUgYXJlIG5vdD8gDQoNCmZ1bmN0aW9uIChEQVRBLCBSQk5ELCBWQVJTID0gTlVMTCwgVW5pcV9LZXlzLCBVU0VfUkVEID0gRkFMU0UsS0VFUF9EQVRBID0gVFJVRSkNCg0KDQpUaGUgYXJndW1lbnRzIHdpdGggcHJlLSBhc3NpZ25lZCB2YWx1ZXMgYXJlIGNhbGxlZCAnRGVmYXVsdCBBcmd1bWVudHMnLiBUaGVzZSBiZWhhdmUganVzdCBsaWtlIHJlZ3VsYXIgYXJndW1lbnRzLCBleGNlcHQgaWYgeW91IGRvbid0IGFzc2lnbiB0aGVtIGEgdmFsdWUgdGhleSB3aWxsIHRha2Ugb24gdGhlIGRlZmF1bHQgdmFsdWUgKGhlbmNlIHRoZSBuYW1lLikNCg0KYGBge3IgZWNobyA9IFRSVUV9DQpyZXR1cm5TdW0gPC0gZnVuY3Rpb24oeCwgeSwgeiA9IDUpIHsNCiAgcmV0dXJuKHggKyB5ICsgeikNCn0NCmBgYA0KDQpgYGB7ciBlY2hvID0gVFJVRX0NCnJldHVyblN1bSgyLCAzKQ0KYGBgDQoNCk5vdGUsIHlvdSBjYW4gc3RpbGwgYXNzaWduIGFueSB2YWx1ZSB0byBkZWZhdWx0IGFyZ3VtZW50cywgaXQgd2lsbCBvdmVyd3JpdGUNCnRoZSBkZWZhdWx0Lg0KDQpgYGB7cn0NCnJldHVyblN1bSgyLCAzLCA2KQ0KYGBgDQoNCllvdSdsbCB0eXBpY2FsbHkgdXNlIGRlZmF1bHQgYXJndW1lbnRzIHdoZW4geW91IGRvbid0IGV4cGVjdCB0aGUgdmFsdWUgb2YNCnRoYXQgdmFyaWFibGUgdG8gY2hhbmdlIG9mdGVuLiBUaGUgdXNlciB3b24ndCBuZWVkIHRvIHNwZWNpZnkgdGhlIA0KYXJndW1lbnQgZXZlcnl0aW1lIHRoZXkgY2FsbCB0aGUgZnVuY3Rpb24uDQoNCioqSU1QT1JUQU5UKioNCg0KVGhlIG9yZGVyIG9mIHRoZSBhcmd1bWVudHMgbWF0dGVycyBpZiB5b3UncmUgbm90IGV4cGxpY2l0bHkgc3BlY2lmeWluZyB0aGVtLg0KTm90aWNlIHdoZW4gd2UndmUgYmVlbiBjYWxsaW5nIG91ciBmdW5jdGlvbnMsIHdlIGhhdmVuJ3QgYmVlbiB3cml0aW5nIHggPSAyLCANCnkgPSAzLCBldGMuIFRoaXMgaXMgYmVjYXVzZSB3ZSdyZSBzdGlja2luZyB0byB0aGUgb3JkZXIgaW4gd2hpY2ggdGhleSBhcHBlYXIgDQp3aXRoaW4gdGhlIGZ1bmN0aW9uIGRlZmluaXRpb24uIA0KDQpgYGB7ciwgZXZhbD1GQUxTRX0NCiNMb29rIGF0IHdoYXQgaGFwcGVucyBpZiB3ZSBkb24ndCByZXNwZWN0IHRoZSBvcmRlcjoNCnJldHVyblN1bSA8LSBmdW5jdGlvbih4LCB5ID0gNSwgeikgew0KICByZXR1cm4oeCArIHkgKyB6KQ0KfQ0KDQpyZXR1cm5TdW0oNCwgNikNCiNUaGlzIHdpbGwgbm90IHJ1bg0KYGBgDQoNCldpbGwgcmV0dXJuOiA8c3BhbiBzdHlsZT0iY29sb3I6cmVkIj4gRXJyb3IgaW4gcmV0dXJuU3VtKDQsIDYpIDogYXJndW1lbnQgInoiIGlzIG1pc3NpbmcsIHdpdGggbm8gZGVmYXVsdCA8L3NwYW4+DQoNClkgaGFzIGEgZGVmYXVsdCB2YWx1ZSwgYnV0IGJlY2F1c2Ugd2UgaGF2ZW4ndCBzcGVjaWZpZWQgYXNzaWdubWVudHMsIDYgaXMgDQpiZWluZyBhc3NpZ25lZCB0byB5IChvdmVyd3JpdGluZyB0aGUgZGVmYXVsdCkgYW5kIHogaXMgbGVmdCB1bmRlZmluZWQuIFRvDQpwcmV2ZW50IHRoaXMsIGV4cGxpY2l0bHkgc3BlY2lmeSB5b3VyIGFzc2lnbm1lbnRzOg0KDQpgYGB7cn0NCnJldHVyblN1bSg0LCB6ID0gNikNCmBgYA0KSG93ZXZlciBzaW5jZSB3ZSBzcGVjaWZpZWQgdGhhdCB6PTYsIHRoZSBjb2RlIHdpbGwgcnVuLg0KDQoNCkdvb2QgcHJhY3RpY2UgZGljdGF0ZXMgdGhhdCB5b3Ugc2hvdWxkIHN0YXJ0IG1ha2luZyB5b3VyIGFzc2lnbm1lbnRzIGV4cGxpY2l0DQphZnRlciB0aGUgc2Vjb25kIGFyZ3VtZW50OyB3aGljaCBhbHNvIG1lYW5zIGlmIHlvdSdyZSB3cml0aW5nIGZ1bmN0aW9ucywgDQpkb24ndCBwdXQgZGVmYXVsdCB2YWx1ZXMgZm9yIHlvdXIgZmlyc3QgdHdvIGFyZ3VtZW50cy4NCg0KTGV0J3MgZGl2ZSBhIGJpdCBtb3JlIGludG8gdGhlIGJvZHkgb2YgdGhlIGZ1bmN0aW9uIG5vdy4gQXQgdGhlaXIgY29yZSwgDQpmdW5jdGlvbnMgYXJlIGEgc2V0IG9mIGluc3RydWN0aW9ucyBwZXJmb3JtZWQgb24gYSBzZXQgb2YgdmFyaWFibGVzIHRvIA0KZ2VuZXJhdGUgYSBkZXNpcmVkIG91dGNvbWUuIFdlJ3ZlIGp1c3QgY292ZXJlZCBhcmd1bWVudHMsIHdoZXJlIHlvdSBhc3NpZ24NCnZhbHVlcyB0byB0aGUgc2V0IG9mIHZhcmlhYmxlcywgc28gbG9naWNhbHkgdGhlIGJvZHkgaXMgd2hlcmUgd2UgZGVmaW5lIA0KdGhlIHNldCBvZiBpbnN0cnVjdGlvbnMuDQoNCiMjIEVEQSBGdW5jdGlvbg0KDQpMZXQncyByZWZlciB0byB0aGUgRURBIGxlc3NvbiBhbmQgc2VlIGlmIHdlIGNhbiBjb252ZXJ0IHNvbWUgcGFydHMgaW50byBmdW5jdGlvbnMuDQpBIGdvb2QgcnVsZSBvZiB0aHVtYiBpcyB0aGF0IGlmIHlvdSdyZSByZXBlYXRpbmcgc29tZXRoaW5nIDMgb3IgbW9yZSB0aW1lcywNCnlvdSBjYW4gcHJvYmFibHkgY29udmVydCBpdCBpbnRvIGEgZnVuY3Rpb24uDQoNCg0KTGV0J3MgbG9vayBhdCB0aGUgRGF0YSBNYW5pcHVsYXRpb24gc3RlcCBvZiB0aGUgRURBIGxlc3Nvbi4gU3BlY2lmaWNhbGx5LCANCnRoaXMgY2h1bmsgb2YgY29kZToNCmBgYHtyfQ0KQUdHLlZFSCA8LSBEQVRBX1Njb3JlZCAlPiUNCiAgZ3JvdXBfYnkoQ2x0X0luc3VyZXJfVHgsDQogICAgICAgICAgIENsdF9BY2NvdW50X05vLCANCiAgICAgICAgICAgUG9sX1BvbGljeV9ObywgDQogICAgICAgICAgIFZlaF9JZF9ObykgJT4lDQogIHN1bW1hcmlzZShMQ19WZWhfVG90YWwgPSBzdW0oTENfVE9UQUwpKQ0KDQpBR0cuSU5TVVJFUiA8LSBEQVRBX1Njb3JlZCAlPiUgDQogIGxlZnRfam9pbihBR0cuVkVIKSAlPiUNCiAgZmlsdGVyKENsb19TYWxlX0luID09IDEgJiBEcmlfVHlwZV9DZCA9PSAnUCcpICU+JSAjIFRvIGhhdmUgdGhlIGRhdGEgYnkgdmVoaWNsZQ0KICBzZWxlY3QoTENfVmVoX1RvdGFsLA0KICAgICAgICAgUHJtX1RybV9WZWhfVG90X0FtLA0KICAgICAgICAgQ2x0X0luc3VyZXJfVHgpICU+JSANCiAgZ3JvdXBfYnkoQ2x0X0luc3VyZXJfVHgpICU+JSANCiAgc3VtbWFyaXNlKFBybV9Ucm1fVmVoX01lYW4gPSBtZWFuKFBybV9Ucm1fVmVoX1RvdF9BbSksDQogICAgICAgICAgICBMUl9WZWhfTWVhbiAgPSBzdW0oTENfVmVoX1RvdGFsLCBuYS5ybSA9IFRSVUUpL3N1bShQcm1fVHJtX1ZlaF9Ub3RfQW0pLA0KICAgICAgICAgICAgWHBvX1ZlaF9OYiAgID0gc3VtKG4oKSkpDQpgYGANCg0KYGBge3IgfQ0KcHJpbnQoQUdHLklOU1VSRVIpDQpgYGANCg0KYGBge3IgfQ0KcHJpbnQoQUdHLklOU1VSRVIpDQpgYGANCg0KYGBge3J9DQpBR0cuRFJJX0FHRSA8LSBEQVRBX1Njb3JlZCAlPiUgDQogIGxlZnRfam9pbihBR0cuVkVIKSAlPiUNCiAgZmlsdGVyKENsb19TYWxlX0luID09IDEgJiBEcmlfVHlwZV9DZCA9PSAnUCcpICU+JSAjIFRvIGhhdmUgdGhlIGRhdGEgYnkgdmVoaWNsZQ0KICBzZWxlY3QoTENfVmVoX1RvdGFsLA0KICAgICAgICAgUHJtX1RybV9WZWhfVG90X0FtLA0KICAgICAgICAgRHJpX01hcml0YWxfU3RhdHVzX0luLA0KICAgICAgICAgRHJpX0FnZV9OYikgJT4lIA0KICBncm91cF9ieShEcmlfTWFyaXRhbF9TdGF0dXNfSW4sDQogICAgICAgICAgIERyaV9BZ2VfTmIpICU+JSANCiAgc3VtbWFyaXNlKFBybV9Ucm1fVmVoX01lYW4gPSBtZWFuKFBybV9Ucm1fVmVoX1RvdF9BbSksDQogICAgICAgICAgICBMUl9WZWhfTWVhbiAgPSBzdW0oTENfVmVoX1RvdGFsLCBuYS5ybSA9IFRSVUUpL3N1bShQcm1fVHJtX1ZlaF9Ub3RfQW0pLA0KICAgICAgICAgICAgWHBvX1ZlaF9OYiAgID0gc3VtKG4oKSkpDQpgYGANCg0KYGBge3J9DQpoZWFkKEFHRy5EUklfQUdFKQ0KYGBgDQoNCmBgYHtyfQ0KaGVhZChBR0cuRFJJX0FHRSkNCmBgYA0KDQoqKkZ1bmN0aW9uIGZvciBMb3NzIFJhdGlvcyBieSBWZWhpY2xlKioNCg0KQXNzdW1pbmcgd2UncmUgaW50ZXJlc3RlZCBpbiBiZWluZyBhYmxlIHRvIGxvb2sgYXQgb3VyIGxvc3MgcmF0aW9zIGJ5IGRyaXZlciBidXQgd2FudCB0byBiZSBhYmxlIHRvIGNvbmR1Y3QgdGhlIGFuYWx5c2lzIG11bHRpcGxlIHRpbWVzLCB3ZSB3b3VsZCBuZWVkIG91ciBkYXRhLCB2YXJpYWJsZXMgYW5kIGRyaXZlciB0eXBlIHRvIGJlIGZsZXhpYmxlLCBzbyB3ZSB0aHVzIGluY2x1ZGUgdGhlc2UgYXMgYXJndW1lbnRzIGluIG91ciBmdW5jaXRvbi4NCg0KYGBge3IgZWNobyA9IFRSVUV9DQphZ2dyZWdhdGVWYXJzIDwtIGZ1bmN0aW9uKERBVEEsIEtFRVAuVkFSUywgR1JPVVAuVkFSUywgRFJJVkVSLlRZUEUgPSAnUCcpIHsNCiAgDQogICMgSXQgZG9lc24ndCBtYXR0ZXIgd2hhdCB5b3UgY2FsbCB5b3VyIGRhdGEgd2l0aGluIHRoZSBmdW5jdGlvbiwgDQogICMgSSBsaWtlIHRvIHVzZSB0ZW1wDQogIHRlbXAgPC0gREFUQSAlPiUgDQogICAgbGVmdF9qb2luKEFHRy5WRUgpICU+JQ0KICAgIGZpbHRlcihDbG9fU2FsZV9JbiA9PSAxICYgRHJpX1R5cGVfQ2QgPT0gRFJJVkVSLlRZUEUpICU+JSANCiAgICBzZWxlY3QoTENfVmVoX1RvdGFsLCAjIFJlcXVpcmVkIHZhcg0KICAgICAgICAgICBQcm1fVHJtX1ZlaF9Ub3RfQW0sICMgUmVxdWlyZWQgdmFyDQogICAgICAgICAgICMgb25lX29mIGlzIGEgZHBseXIgZnVuY3Rpb24gZGVzaWduZWQgdG8gYmUgdXNlZCB3aXRoIFNlbGVjdA0KICAgICAgICAgICBvbmVfb2YoS0VFUC5WQVJTKSkgJT4lIA0KICAgICMgTm90aWNlIHdlJ3JlIHVzaW5nIGdyb3VwX2J5X2F0IGluc3RlYWQgb2YgZ3JvdXBfYnkgbm93DQogICAgIyBTb21lIGRwbHlyIGZ1bmN0aW9ucyBoYXZlIHZlcnNpb25zIGRlc2lnbmVkIGZvciBmdW5jdGlvbmFsIA0KICAgICMgcHJvZ3JhbW1pbmc7IHRoaXMgaXMgb25lLg0KICAgIGdyb3VwX2J5X2F0KEdST1VQLlZBUlMpICU+JSANCiAgICBzdW1tYXJpemUoUHJtX1RybV9WZWhfTWVhbiA9IG1lYW4oUHJtX1RybV9WZWhfVG90X0FtKSwNCiAgICAgICAgICAgICAgTFJfVmVoX01lYW4gID0gc3VtKExDX1ZlaF9Ub3RhbCwgbmEucm0gPSBUUlVFKS9zdW0oUHJtX1RybV9WZWhfVG90X0FtKSwNCiAgICAgICAgICAgICAgWHBvX1ZlaF9OYiAgID0gc3VtKG4oKSkpDQogIA0KICAjIFdlJ2xsIGNvdmVyIHJldHVybiBpbiBhIG1vbWVudA0KICByZXR1cm4odGVtcCkNCn0NCmBgYA0KDQpOb3cgd2UgY2FuIGRlZmluZSBvdXIgS0VFUC5WQVJTIGFuZCBHUk9VUC5WQVJTDQpXZSdyZSBkZWZpbmluZyB0aGUgY29sdW1uIG5hbWVzIGFzIHN0cmluZ3MsIFIga25vd3Mgd2hhdCB0byBkbyB0aG91Z2gNCg0KYGBge3IgZWNobyA9IFRSVUV9DQpLRUVQLlZBUlMgPC0gYygiRHJpX01hcml0YWxfU3RhdHVzX0luIiwgIkRyaV9BZ2VfTmIiKQ0KR1JPVVAuVkFSUyA8LSBLRUVQLlZBUlMgDQpgYGANCg0KQW5kIGNhbGwgb3VyIGZ1bmN0aW9uIA0KYGBge3J9DQpBR0cuRFJJX0FHRV9GdW5jdGlvbl9PdXQgPC0gYWdncmVnYXRlVmFycygNCiAgREFUQSA9IERBVEFfU2NvcmVkLCANCiAgS0VFUC5WQVJTLCANCiAgR1JPVVAuVkFSUyA9IEdST1VQLlZBUlMNCiAgKQ0KYGBgDQoNCk5vdGljZSB0aGF0IHRoaXMgb3V0cHV0cyB0aGUgZXhhY3Qgc2FtZSB0aGluZyBhcyB0aGUgYWJvdmUgY2h1bmsgb2YgY29kZS4NClRoZSBkaWZmZXJlbmNlIGlzLCBub3cgd2UgY2FuIHBlcmZvcm0gdGhlIHNhbWUgYW5hbHlzaXMgb24gZGlmZmVyZW50IHZhcmlhYmxlcyB3aXRoIG1pbmltYWwgZWZmb3J0Lg0KYGBge3J9DQpLRUVQLlZBUlMgPC0gYygiRHJpX0dlbmRlcl9DZCIsICJEcmlfWXJzX0xpY2Vuc2VkX0FVX05iIikNCkdST1VQLlZBUlMgPC0gS0VFUC5WQVJTIA0KDQpBR0cuWXJzX0xpY19GdW5jdGlvbl9PdXQgPC0gYWdncmVnYXRlVmFycygNCiAgREFUQV9TY29yZWQsIA0KICBLRUVQLlZBUlMsIA0KICBHUk9VUC5WQVJTID0gR1JPVVAuVkFSUw0KKQ0KYGBgDQoNCkhvcGVmdWxseSB0aGlzIGV4YW1wbGUgaGFzIGRlbW9uc3RyYXRlZCB0aGUgZmxleGliaWxpdHkgYW5kIHVzZWZ1bG5lc3Mgb2YgDQpjcmVhdGluZyBjdXN0b20gZnVuY3Rpb25zLiBUaGUga2V5LXRha2Vhd2F5IGlzIHRoYXQgaWYgeW91IGNhbiBzY3JpcHQgaXQsIA0KeW91IGNhbiB0dXJuIGl0IGludG8gYSBmdW5jdGlvbiBmb3IgcmUtdXNlYWJpbGl0eS4NCg0KIyMgVmFyaWFibGUgU2NvcGUNCg0KVmFyaWFibGUgc2NvcGUgaXMgYSBwcmV0dHkgY29tcGxleCB0b3BpYywgYnV0IGZvciBtb3N0IHVzZSBjYXNlcyBpbiBvdXINCmpvYiBrbm93aW5nIHRoZSBiYXNpY3MgaXMgc3VmZmljaWVudC4gVGhlIGVhc2llc3Qgd2F5IHRvIGV4cGxhaW4gDQp2YXJpYWJsZSBzY29wZSBpcyB3aXRoIGFuIGV4YW1wbGUuDQoNCk9ic2VydmUNCmBgYHtyfQ0KeSA8LSA3DQoNCmRlbW9GdW5jdGlvbiA8LSBmdW5jdGlvbih4KSB7DQoNCiAgcHJpbnQocGFzdGUoIlRoZSB2YWx1ZSBvZiB4IGlzICIsIHgsICIgYW5kIHRoZSB2YWx1ZSBvZiBZIGlzICIsIHkpLCBzZXAgPSAiIikNCiAgDQp9DQpgYGANCiAgDQpgYGB7cn0NCmRlbW9GdW5jdGlvbig4KQ0KYGBgDQoNCmBgYHtyfQ0Kcm0oeSkgIyBUbyBkZWxldGUgeQ0KYGBgDQoNCldlIGNhbiBzZWUgdGhhdCBldmVuIHRob3VnaCB5IGlzIG5vdCBkZWZpbmVkIHdpdGhpbiB0aGUgZnVuY3Rpb24sIGRlbW9GdW5jdGlvbiBpcyBzdGlsbCBhYmxlIHRvIGZpbmQgdGhlIHkgdmFsdWUgdGhhdCB3YXMgZGVjbGFyZWQgb3V0c2lkZSBvZiB0aGUgZnVuY3Rpb24uIFRoaXMgaXMgYmVjYXVzZSBvZiB2YXJpYWJsZSBzY29wZS4gQnkgZGVmYXVsdCwgdGhlIHVzZXIgaXMgd29ya2luZyBpbiBzb21ldGhpbmcgY2FsbGVkIHRoZSBHbG9iYWwgRW52aXJvbm1lbnQgYW5kIGZ1bmN0aW9ucyB3aWxsIGhhdmUgYWNjZXNzIHRvIHRoZSB2YXJpYWJsZXMgZGVjbGFyZWQgaW4gb3VyIGdsb2JhbCBlbnZpcm9ubWVudC4gTm93IGlmIHdlIHdhbnQgdG8gbG9vayBhdCBhbm90aGVyIGV4YW1wbGU6DQoNCmBgYHtyfQ0KeSA8LSA2DQpkZW1vRnVuY3Rpb24yIDwtIGZ1bmN0aW9uKCkgew0KICB5IDwtIDQNCiAgeQ0KfQ0KYGBgDQogDQpgYGB7cn0NCmRlbW9GdW5jdGlvbjIoKQ0KYGBgDQoNCldlIGNhbiBzZWUgdGhhdCB0aGUgZnVuY3Rpb24gYXNzaWducyB5IGEgdmFsdWUgb2YgNCB3aXRoaW4gdGhlIGZ1bmN0aW9uIGFuZCB3aWxsIHJldHVybiB0aGlzIHZhbHVlIG9mIHkgd2hpY2ggaXMgNC4gDQoNCmBgYHtyfQ0KeQ0KYGBgDQpIb3dldmVyLCB3aGVuIHdlIGxvb2sgYXQgeSBvdXRzaWRlIG9mIHRoZSBmdW5jdGlvbiB5IGlzIHN0aWxsIDYuIFRoaXMgYWdhaW4gaGFzIHRvIGRvIHdpdGggdGhlIGVudmlyb25tZW50IGFuZCB0aGUgZmFjdCB0aGF0IGFzc2lnbmluZyB5IHRoZSB2YWx1ZSBvZiA0IHdpdGhpbiB0aGUgZnVuY3Rpb24gd2lsbCBub3QgY2hhbmdlIHRoZSB2YWx1ZSBvZiB5IGluIHRoZSBnbG9iYWwgZW52aXJvbm1lbnQuDQoNCiMjIFZhcmlhYmxlIFJldHVybnMNCg0KV2UgbWVudGlvbmVkIGFib3ZlIHRocm91Z2ggb3VyIG1vc3QgcmVjZW50IGV4YW1wbGUgdGhhdCBlbnZpb3JubWVudHMgZG8gbm90IHBhc3MgYWxsIHRoZWlyIG9iamVjdHMgb250byB0aGUgZ2xvYmFsIGVudmlyb25tZW50LiBTbyBob3cgZG8geW91IGdldCBvYmplY3RzIGZyb20gYSBjaGlsZCBlbnZpcm9ubWVudCB0byBpdHMgcGFyZW50PyANCiANClRoZSBhbnN3ZXIgaXMgd2l0aCB0aGUgcmV0dXJuIGZ1bmN0aW9uLiBXaGVuIHlvdSBjYWxsIGEgZnVuY3Rpb24sIGl0IGlzIGdvaW5nIHRvIHJldHVybiBzb21ldGhpbmcgYXQgdGhlIGVuZCBvZiBpdHMgcnVuLiBJdCBtYXkgY3JlYXRlIG1hbnkgb2JqZWN0cyBhbG9uZyB0aGUgd2F5LCBidXQgYXQgaXRzIGVuZCBpdCB3aWxsIG9ubHkgcmV0dXJuIG9uZSBvYmplY3QgKCB0aGF0IG9iamVjdCBjYW4gYmUgYSBsaXN0IG9mIG1hbnkgb2JqZWN0cy4pIFRoZSByZXR1cm4gZnVuY3Rpb24gaXMgeW91ciB3YXkgb2YgdGVsbGluZyBmdW5jdGlvbiB3aGF0IHlvdSB3YW50IHRoZW0gdG8gcmV0dXJuLiANCg0KYGBge3J9DQpyZXR1cm5WYWx1ZSA8LSBmdW5jdGlvbih4LCB5KSB7DQogIA0KICB6IDwtIHggKiB5DQogIA0KICByZXR1cm4oeikNCn0NCmBgYA0KDQpgYGB7cn0NCnJldHVyblZhbHVlKDIsIDUpDQpgYGANCg0KSXQgaXMgbm90IGVudGlyZWx5IG5lY2Vzc2FyeSB0byB1c2UgdXNlIHJldHVybigpIGluIGZ1bmN0aW9ucy4gSWYgdGhlcmUgaXMgbm8gcmV0dXJuIHNwZWNpZmllZCwgeW91ciBmdW5jdGlvbiB3aWxsIHJldHVybiB0aGUgbGFzdCB0aGluZyBpdCBjYWxscyBpbnRlcm5hbGx5Lg0KDQpgYGB7cn0NCnJldHVyblZhbHVlIDwtIGZ1bmN0aW9uKHgsIHkpIHsNCiAgDQogIHogPC0geCAqIHkNCiAgDQogIHoNCn0NCmBgYA0KDQpgYGB7cn0NCnJldHVyblZhbHVlKDIsIDUpDQpgYGANCiANClRoZSBhYm92ZSBmdW5jdGlvbiB3aWxsIHdvcmsganVzdCBmaW5lIGFuZCB3aWxsIHJldHVybiB6LCBqdXN0IGFzIHdlIHdhbnQuIFVzaW5nIHJldHVybigpIGlzIG9ubHkgcmVjb21tZW5kZWQgYmVjYXVzZSBpdCBoZWxwcyB3aXRoIGNvbXByZWhlbnNpb24gb2YgeW91ciBjb2RlLiBJZiB5b3UncmUgd3JpdGluZyBmdW5jdGlvbnMgd2hpY2ggd2lsbCBiZSB1c2VkIGJ5IHVzZXJzIG90aGVyIHRoYW4geW91cnNlbGYsIGl0IGlzIGEgZ29vZCBpZGVhIHRvIGluY2x1ZGUgcmV0dXJuKCkgY2FsbHMuICAgICANCiANCkZpbmFsbHksIGJlY2F1c2UgZnVuY3Rpb25zIGFyZSByZXR1cm5pbmcgYSBzaW5nbGUgb2JqZWN0LCB5b3UgbmVlZCB0byBtYWtlIHN1cmUgeW91J3JlIA0Kc3RvcmluZyB0aGF0IG9iamVjdCBpbiB5b3VyIGdsb2JhbCBlbnZpcm9ubWVudCBpZiB5b3UgaW50ZW5kIHRvIHVzZSBpdCBsYXRlci4NCg0KYGBge3J9DQp3IDwtIHJldHVyblZhbHVlKDIsIDUpICMgU3RvcmUgdGhlIG91dHB1dCBvYmplY3QgZnJvbSByZXR1cm5WYWx1ZSBpbiBhIHZhcmlhYmxlIGNhbGxlZCB3DQpwIDwtIHcqMw0KYGBgDQoNCmBgYHtyfQ0KcA0KYGBgDQoNCiMgRXhhbXBsZXMNCg0KV2UndmUgYmVlbiBhYmxlIHRvIHNlZSBzb21lIGZ1bmN0aW9ucyBhbmQgaGF2ZSBkZXRlcm1pbmVkIHRoYXQgdGhleSBhcmUgdXNlZnVsDQpmb3IgcmVzdWFiaWxpdHkgb2YgY29kZSBhbmQgcGVyZm9ybWluZyB0YXNrcy4gV2Ugd2FudCB0byBjb250aW51ZSB0aGlzIG1pbmQgc2V0DQphbmQgc2VlIGhvdyBzb21lIHNpbXBsZSBjb2RlIGNhbiBiZSBzYXZlZCB0byB0YWNrbGUgcHJvYmxlbXMgdGhhdCB3aWxsIGJlIGZhY2VkDQptdWx0aXBsZSB0aW1lcy4NCg0KIyMgQml2YXJpYXRlIEFuYWx5c2lzDQoNCiMjIyBFeHBsYW5hdGlvbg0KTGV0IHVzIHNlZSBhIHNpbXBsZSBleGFtcGxlIG9mIGEgZnVuY3Rpb24gdGhhdCB3aWxsIHBlcmZvcm0gYW4gaW5pdGlhbCBiaXZhcmlhdGUgYW5hbHlzaXMgb2Ygb3VyIGRhdGEgc2V0LiBXZSB3YW50IHNvbWV0aGluZyBmbGV4aWJsZSB0aGF0IHdpbGwgYWxsb3cgdXMgdG8gcGVyZm9ybSBhbmFseXNpcyByZWdhcmRsZXNzIG9mIG91ciBkYXRhIHNldCBhbmQgcmVzcG9uc2UgdmFyaWFibGUuDQoNClRoZSBhcmd1bWVudHMgZm9yIHRoaXMgZnVuY3Rpb24gaW5jbHVkZToNCg0KDQoxKSBEQVRBOiBUaGUgZGF0YSB0aGF0IHdlIHdpbGwgYmUgdXNpbmcuIGV4cDogREFUQSA9IERBVEFfUVVPX0FVVE8NCg0KDQoyKSB4OiBUaGUgbmFtZSBvZiB0aGUgeCB2YXJhaWJsZS4gIGV4cDogeCA9IERyaV9BZ2VfTmINCg0KDQozKSB5OiBUaGUgbmFtZSBvZiB0aGUgeSB2YXJpYWJsZS4gRXhhbXBsZXMgc3VjaCBhcyBsb3NzIGNvc3Qgb3IgY2xvc2luZywgZXhwOiB5ID0gQ2xvX1NhbGVfSW4NCg0KDQpUaGVzZSBmdW5jdGlvbnMgYmVsb3cgd2lsbCBjb21iaW5lIHdoYXQgaGFzIGJlZW4gc2VlbiBzbyBmYXIgdG8gcHJvdmlkZSBzb21lIGV4YW1wbGVzIG9mIGVhc3kgdG8gdXNlIGZ1bmN0aW9ucy4NCg0KIyMjIEJyZWFrZG93biBvZiBGdW5jdGlvbiBFeGFtcGxlDQpUaGlzIGZ1bmN0aW9uIGV4YW1wbGUgaGVyZSBwcm92aWRlcyB1c2VycyB3aXRoIHRoZSBhYmlsaXR5IHRvIHNlZSBob3cgYSByZXNwb25zZSB2YXJpYWJsZSBzdWNoIGFzIGNsb3NpbmcgaXMgZGlmZmVyZW50IGFjcm9zcyBhIHZhcmlhYmxlIG9mIG91ciBjaG9pY2UuIFdlIHdpbGwga2VlcCB5IGFzIGEgZGVmYXVsdCB2YXJpYWJsZSBvZiBDbG9fU2FsZV9JbiBidXQgYXMgcHJldmlvdXNseSBtZW50aW9uZWQgdGhlIHkgYXJnbWVudCBjYW4gYmUgY2hhbmdlZCBpZiBuZWVkZWQuDQoNCkRvbid0IHdvcnJ5IHRvbyBtdWNoIGFib3V0ICEhIGFuZCBlbnF1bywgYnV0IHRoaXMgaXMgc2ltcGx5IGEgdGVjaG5pcXVlIHdlIGNhbiB1c2UgdG8gcGFzcyB0aGUgbmFtZSBvZiB0aGUgZGF0YSBmcmFtZSB2YXJpYWJsZSB3aXRob3V0IHVzaW5nIHF1b3RhdGlvbnMgIiIuDQpgYGB7cn0NCnN1bW1hcnlTdGF0cyA8LSBmdW5jdGlvbihEQVRBLHgseSA9IENsb19TYWxlX0luKXsNCiAgREFUQSAlPiUNCiAgICBncm91cF9ieSghISBlbnF1byh4KSkgJT4lDQogICAgc3VtbWFyaXplKE1lYW4gPSBtZWFuKCEhZW5xdW8oeSksIG5hLnJtPVRSVUUpLA0KICAgICAgICAgICAgICBDb3VudCA9IG4oKSkNCn0NCg0KYGBgDQoNCmBgYHtyfQ0Kc3VtbWFyeVN0YXRzKERBVEFfUVVPX0FVVE8sQ2x0X0hvbWVfTWFpbl9Mb2NhdGlvbl9DZCkNCmBgYA0KDQpgYGB7cn0NCnN1bW1hcnlTdGF0cyhEQVRBX1FVT19BVVRPLENsdF9Ib21lX01haW5fTG9jYXRpb25fQ2QpDQpgYGANCg0KYGBge3J9DQpzdW1tYXJ5U3RhdHMoREFUQV9RVU9fQVVUTyxDbHRfU2VnbWVudCkNCmBgYA0KDQpgYGB7cn0NCnN1bW1hcnlTdGF0cyhEQVRBX1FVT19BVVRPLENsdF9TZWdtZW50KQ0KYGBgDQoNCmBgYHtyfQ0Kc3VtbWFyeVN0YXRzKERBVEFfUVVPX0FVVE8sVmVoX01vZGVsX1lyX0R0LENsb19TYWxlX0luKQ0KYGBgDQoNCmBgYHtyfQ0Kc3VtbWFyeVN0YXRzKERBVEFfUVVPX0FVVE8sVmVoX01vZGVsX1lyX0R0LENsb19TYWxlX0luKQ0KYGBgDQoNCg0KDQoNCiMjIFBsb3R0aW5nIA0KDQpXZSB3aWxsIHVzZSB0aGUgcGxvdHRpbmcgZnVuY3Rpb24gcGxvdGx5IGFzIHNlZW4gaW4gcHJldmlvdXMgZXhhbXBsZXMgdGhhdCB3aWxsIGFsbG93IHVzIHRvIGhhdmUgYSBmbGV4aWJsZSBmdW5jdGlvbiBmb3IgeSB2cyB4IHBsb3R0aW5nLiBXZSBjYW4gY3JlYXRlZCB0aGUgZ3JvdXBlZCBkYXRhIGluIHRoZSBleGFjdCBzYW1lIHdhcyBhcyBwcmV2aW91c2x5IGJlZm9yZSB3ZSBwbG90IGl0DQoNCmBgYHtyfQ0KcGxvdGx5RGF0YSA8LSBmdW5jdGlvbihEQVRBLHgseSA9IENsb19TYWxlX0luKSB7DQoNCkdyb3VwZWRfRGF0YSA9IERBVEEgJT4lDQogIGdyb3VwX2J5KCEhZW5xdW8oeCkpICU+JQ0KICBzdW1tYXJpemUoUmVzcG9uc2VfTWVhbiA9IG1lYW4oISFlbnF1byh5KSwgbmEucm09VFJVRSksDQogICAgICAgICAgICBDb3VudCA9IG4oKSkNCnAgPC0gcGxvdF9seShkYXRhID0gR3JvdXBlZF9EYXRhKSAlPiUNCiAgI2FkZF9saW5lcyB3aWxsIHByb3ZpZGUgdGhlIG1lYW4gcmVzcG9uc2UgYXQgZWFjaCBncm91cA0KICBhZGRfbGluZXMoDQogICAgeCA9ICBlbnF1byh4KSwNCiAgICB5ID1+IFJlc3BvbnNlX01lYW4sDQogICAgbmFtZSA9Ik9ic2VydmVkIE1lYW4iDQogICkgJT4lIA0KICBhZGRfYmFycygNCiAgICB4ID0gIGVucXVvKHgpLA0KICAgIHkgPX4gIENvdW50LA0KICAgIHlheGlzID0gInkyIiwNCiAgICBvcGFjaXR5ID0gMC4zNSwNCiAgICBuYW1lID0gIkV4cG9zdXJlIg0KICApICU+JQ0KICBsYXlvdXQoDQogICAgdGl0bGUgPSBwYXN0ZTAoZW5leHByKHkpLCAiIHZzICIsIGVuZXhwcih4KSksDQogICAgeGF4aXMgPSBsaXN0KHRpdGxlID0gcGFzdGUwKGVuZXhwcih4KSkpLA0KICAgIHlheGlzID0gbGlzdCh0aXRsZSA9IHBhc3RlMChlbmV4cHIoeSkpLCBzaWRlID0gImxlZnQiLCBvdmVybGF5aW5nID0gInkyIiksDQogICAgYmFybW9kZSA9ICJzdGFjayIsDQogICAgeWF4aXMyID0gbGlzdCh0aXRsZSA9ICJFeHBvc3VyZSIsIHNpZGUgPSAicmlnaHQiKQ0KICApDQpyZXR1cm4ocCkNCn0NCg0KYGBgDQoNCg0KV2UgY2FuIG5vdyB0cnkgb3V0IG91ciBwbG90IGZ1bmN0aW9uIHRvIGhhdmUgcGxvdHMgZ2VuZXJhdGVkIGFuZCBzYXZlZCBpbiByZXNwZWN0aXZlIHZhcmlhYmxlcy4NCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgaW5jbHVkZT1UUlVFfQ0KUGxvdF9EcmlfQWdlX05iIDwtIHBsb3RseURhdGEoREFUQV9RVU9fQVVUTyxEcmlfQWdlX05iKQ0KUGxvdF9DbHRfSG9tZV9NYWluX0xvY2F0aW9uX0NkPC1wbG90bHlEYXRhKERBVEFfUVVPX0FVVE8sQ2x0X0hvbWVfTWFpbl9Mb2NhdGlvbl9DZCkNClBsb3RfRHJpX1ViaV9JbiA8LSBwbG90bHlEYXRhKERBVEFfUVVPX0FVVE8sRHJpX1ViaV9JbikNCmBgYA0KDQpBbmQgd2UgY2FuIG5vdyBsb29rIGF0IHRoZXNlIHBsb3RzLg0KDQpgYGB7cn0NClBsb3RfRHJpX0FnZV9OYg0KYGBgDQoNCmBgYHtyfQ0KUGxvdF9EcmlfQWdlX05iDQpgYGANCg0KYGBge3J9DQpQbG90X0NsdF9Ib21lX01haW5fTG9jYXRpb25fQ2QNCmBgYA0KDQpgYGB7cn0NClBsb3RfQ2x0X0hvbWVfTWFpbl9Mb2NhdGlvbl9DZA0KYGBgDQoNCmBgYHtyfQ0KUGxvdF9EcmlfVWJpX0luDQpgYGANCg0KYGBge3J9DQpQbG90X0RyaV9VYmlfSW4NCmBgYA0KDQpUaGVzZSBhcmUgMyBleGFtcGxlcyBvZiBmdW5jdGlvbnMgdGhhdCB5b3UgY291bGQgY3JlYXRlIHlvdXJzZWxmLiBGcm9tIHRoZSB1c2Ugb2Ygb3RoZXIgZnVuY3Rpb25zIGFuZCBwcmV2aW91c2x5IHdyaXR0ZW4gY29kZSwgeW91IGNvdWxkIGNyZWF0ZSBmdW5jdGlvbnMgdGFpbG9yZWQgdG8geW91ciBldmVyeWRheSBuZWVkcy4NCg0KRmluYWxseSB3ZSB3YW50IHRvIHRyeSB0byBleHBvcnQgdGhlc2UgcGxvdHMuDQpgYGB7cn0NCiNDaGFuZ2UgdGhpcyB0byBhIHBhdGggdGhhdCB3b3JrcyBmb3IgeW91IHRvIGV4cG9ydCB0bw0Kc2V0d2QoIkM6L1VzZXJzL29wdGkxNDA3L09uZURyaXZlIC0gVGhlIFRvcm9udG8tRG9taW5pb24gQmFuay9Eb2N1bWVudHMiKQ0KDQpodG1sd2lkZ2V0czo6c2F2ZVdpZGdldChQbG90X0RyaV9BZ2VfTmIsICJQbG90X0RyaV9BZ2VfTmIuaHRtbCIpDQpodG1sd2lkZ2V0czo6c2F2ZVdpZGdldChQbG90X0NsdF9Ib21lX01haW5fTG9jYXRpb25fQ2QsICJQbG90X0NsdF9Ib21lX01haW5fTG9jYXRpb25fQ2QuaHRtbCIpDQpodG1sd2lkZ2V0czo6c2F2ZVdpZGdldChQbG90X0RyaV9VYmlfSW4sICJQbG90X0RyaV9VYmlfSW4uaHRtbCIpDQpgYGANCg0KDQpJbiBzdW1tYXJ5IG9uY2Ugd2UndmUgd3JpdHRlbiBjb2RlIHdoZXRoZXIgaXQgYmUgZm9yIHJlYmFzaW5nLCBzY29yaW5nIG9yIHF1aWNrbHkgYW5hbHlzaW5nIG91ciBkYXRhLCB3ZSBjYW4gY29udmVydCB0aGlzIGNvZGUgaW50byBhIGZ1bmN0aW9uIHRvIGFsbG93IHVzIHRvIHVzZSBpdCBmb3IgYSB2YXJpZXR5IG9mIHByb2JsZW1zLg0KDQo=